#include "NezhaProfiler.h"
#include "NezhaException.h"

namespace Nezha
{
	Profiler::Profiler(const char* collectorName, u32 subsysIndex)
		:mConstName(collectorName)
		,mNext(NULL)
		,mParent(NULL)
		,mStackLevel(0)
	{
		mThreadID = 0;
		mCount = 0;
		mFrameTime = 0;
		mTotalFrameTime = 0;

		ProfileSystem* ps = ProfileSystem::getSingletonPtr();
		if(ps)
		{
			mSubsystem = ps->getSubSystem(subsysIndex);
			if(mSubsystem)
			{
				mSubsystem->_addProfiler(this);
			}
		}
	}

	void Profiler::start(ProfileScope* scope)
	{
		if(mSubsystem)
		{
			mSubsystem->startProfile(scope);
		}
	}

	void Profiler::end(ProfileScope* scope)
	{
		if(mSubsystem)
		{
			mSubsystem->endProfile(scope);
		}
	}

	ProfileSubSystem::ProfileSubSystem(u32 index)
		:mIndex(index)
		,mListHead(NULL)
		,mListTrail(NULL)
	{
	}

	void ProfileSubSystem::_addProfiler(Profiler* p)
	{
		if(mListTrail)
		{
			mListTrail->mNext = p;
		}

		mListTrail = p;
		if(!mListHead)
		{
			mListHead = p;
		}

	}

	void ProfileSubSystem::resetAllProfilers()
	{
		Profiler* p = mListHead;
		while(p)
		{
			p->mFrameTime = 0;
			p->mStackLevel = 0;
			p = p->mNext;
		}

		mCurrentProfiler = NULL;
	}

	void ProfileSubSystem::startProfile(ProfileScope* scope)
	{
		scope->mStartTime = GetTimeMillisecond();
		scope->mEndTime = scope->mStartTime;

		if(mCurrentProfiler)
		{
			short sl = mCurrentProfiler->mStackLevel;
			scope->mProfiler->mParent = mCurrentProfiler;
			scope->mProfiler->mStackLevel = sl + 1;
		}

		mCurrentProfiler = scope->mProfiler;
	}

	void ProfileSubSystem::endProfile(ProfileScope* scope)
	{
		scope->mEndTime = GetTimeMillisecond();
		u32 timeSpend = scope->mEndTime - scope->mStartTime;
		if(timeSpend < 0)
			timeSpend = 0;

		scope->mProfiler->mFrameTime += timeSpend;
		scope->mProfiler->mTotalFrameTime += timeSpend;
		++scope->mProfiler->mCount;

		if(mCurrentProfiler->mParent)
		{
			mCurrentProfiler = mCurrentProfiler->mParent;
		}
		else
		{
			mCurrentProfiler = NULL;
		}
	}




	NEZHA_SINGLETON_IMPLEMENT(ProfileSystem);

	ProfileSystem::ProfileSystem()
		:mProfiler(NULL)
		,mScope(NULL)
	{

	}

	ProfileSystem::~ProfileSystem()
	{
		if(mProfiler)
		{
			NZ_Delete mProfiler;
		}

		if(mScope)
		{
			NZ_Delete mScope;
		}
	}

	void ProfileSystem::startFrame()
	{	
		for(PMap<u32, ProfileSubSystem*>::iterator it = mSubsystems.begin(); it != mSubsystems.end(); it++)
		{
			it->second->resetAllProfilers();
		}

		if(!mProfiler)
		{
			mProfiler = NZ_New Profiler("ProfileSystem", -1);
		}

		if(!mScope)
		{
			mScope = NZ_New ProfileScope(mProfiler, false);
		}

		mScope->start();
	}

	void ProfileSystem::endFrame()
	{
		if(mScope)
		{
			mScope->end();
		}

		for(PMap<u32, ProfileSubSystem*>::iterator it = mSubsystems.begin(); it != mSubsystems.end(); it++)
		{
			it->second->report();
		}

		// report global profiler?
	}

	ProfileSubSystem* ProfileSystem::getSubSystem(u32 subsysIndex)
	{
		PMap<u32, ProfileSubSystem*>::iterator it = mSubsystems.find(subsysIndex);
		if(it != mSubsystems.end())
		{
			return it->second;
		}

		return NULL;
	}

	void ProfileSystem::addProfileSubsystem(ProfileSubSystem* subsys)
	{
		PMap<u32, ProfileSubSystem*>::iterator it = mSubsystems.find(subsys->getIndex());
		if(it != mSubsystems.end())
		{
			NZ_ASSERT_EX(false, "profile subsystem already registered!!");
			return;
		}

		mSubsystems[subsys->getIndex()] = subsys;
	}

	void ProfileSystem::removeProfileSystem(ProfileSubSystem* subsys)
	{
		PMap<u32, ProfileSubSystem*>::iterator it = mSubsystems.find(subsys->getIndex());
		if(it != mSubsystems.end())
		{
			mSubsystems.erase(it);
		}
	}

}//end namespace Nezha